package app.routing;

import app.domain.EdgeMapper;
import app.domain.VertexMapper;
import app.domain.entity.Edge;
import app.domain.entity.Vertex;
import app.domain.geojson.GeoLineStringDTO;
import app.domain.geojson.GeoPointDTO;
import app.domain.PathQueryCmdParam;
import org.locationtech.jts.geom.Geometry;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * RoutingController
 * @author somebody
 */
@RestController
@RequestMapping("/routing")
public class RoutingController {
    private final VertexMapper vertexMapper;
    private final EdgeMapper edgeMapper;

    public RoutingController(VertexMapper vertexMapper, EdgeMapper edgeMapper) {
        this.vertexMapper = vertexMapper;
        this.edgeMapper = edgeMapper;
    }

    /**
     * 最短路径查询
     */
    @PostMapping("/getShortestPath")
    public Response getShortestPath(@RequestBody PathQueryRequestBody request) {
        PathQueryCmdParam param;

        // 入参检查1
        if (request.getStartingNode() < 0) {
            return new PathQueryResponse(400, "Invalid starting node: "
                    + request.getStartingNode());
        }
        // 入参检查2
        if (request.getDestinationNode() < 0) {
            return new PathQueryResponse(400, "Invalid destination node: "
                    + request.getDestinationNode());
        }
        // 构建 PathQueryCmdParam
        param = new PathQueryCmdParam(request.getStartingNode(), request.getDestinationNode());
        // 执行最短路径查询
        List<Edge> path = edgeMapper.queryShortestPath(param);
        // 检查输出结果
        if (path.isEmpty()) {
            return new PathQueryResponse(404, "Not Found");
        }
        List<GeoLineStringDTO> list = new ArrayList<>(path.size());
        for(Edge e: path) {
            GeoLineStringDTO next = GeoLineStringDTO.fromJsonStr(e.getGeoJson());
            list.add(next);
        }
        // 输出DTO数据
        return new PathQueryResponse(200, "OK", 4326, list);
    }

    /**
     * getEdgeById
     */
    @PostMapping("/getEdgeById")
    public Response getEdgeById(@RequestBody EdgeQueryRequestBody request) {
        Optional<Edge> found;
        GeoLineStringDTO edgeDTO;

        // 入参检查
        if (request.getId() < 0) {
            return new EdgeQueryResponse(400, "Invalid id");
        }
        // 构建并返回多段线LineString
        found = edgeMapper.findById(request.getId());
        if (!found.isPresent()) {
            return new EdgeQueryResponse(404, "Not Found");
        }
        String geoJson = found.get().getGeoJson();
        edgeDTO = GeoLineStringDTO.fromJsonStr(geoJson);
        List<GeoLineStringDTO> list = Collections.singletonList(edgeDTO);
        return new EdgeQueryResponse(200, "OK", 4326, list);
    }

    /**
     * getVertexById
     */
    @PostMapping("/getVertexById")
    public Response getVertexById(@RequestBody VertexQueryRequestBody request) {
        // 入参检查
        if (request.getId() < 0) {
            return new VertexQueryResponse(400, "Invalid id");
        }

        // 构建并返回data
        Optional<Vertex> result = vertexMapper.findById(request.getId());
        if (!result.isPresent()) {
            return new VertexQueryResponse(404, "Not Found");
        }
        Vertex vertex = result.get();
        Geometry geom = vertex.getGeom();
        GeoPointDTO dto = GeoPointDTO.fromGeometry(geom);
        List<GeoPointDTO> list = Collections.singletonList(dto);
        return new VertexQueryResponse(200, "OK", 4326, list);
    }

    public interface Response {
        /**
         * get错误码
         *
         * @return 错误码
         */
        int getCode();

        /**
         * set错误码
         *
         * @param code 自定义错误码, 例如: 404, 200等
         */
        void setCode(int code);

        /**
         * get消息字符串
         *
         * @return message
         */
        String getMsg();

        /**
         * set消息字符串
         *
         * @param msg message
         */
        void setMsg(String msg);
    }

}
